package com.dfs.parent.common.feign.rule;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.dfs.parent.common.core.constant.GrayConstant;
import com.dfs.parent.common.feign.context.GrayContextHolder;
import com.dfs.parent.common.feign.props.GrayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.*;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @Description 灰度规则
 * @Author dahong
 * @Date 2024/2/18 10:31
 * 您调用了 super.choose(...).block()，即 父类的 RoundRobinLoadBalancer.choose()
 * 但父类的 choose() 方法 并不知道您传入的是过滤后的实例列表！
 * 它仍然会从 supplier.get() 获取原始的、未过滤的实例列表！
 * 🔥 也就是说：您虽然传了 filteredInstances，但 super.choose() 并不会使用它！
 * 为什么不能用 super.choose()？
 * 因为 super.choose() 内部会使用原始的 serviceInstanceListSupplier，它不知道您想过滤。
 * 您调用 super.choose(createGrayRequest(...)).block() 是无效的，因为新 request 的 header 不会被 RoundRobinLoadBalancer 读取。
 *
 */
@Slf4j
public class GrayRoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private final String serviceId;
    private final AtomicInteger position;
    // ✅ 注入默认的 RoundRobinLoadBalancer 的逻辑
    private final RoundRobinLoadBalancer defaultLoadBalancer;
    private final GrayProperties grayProperties;

    public GrayRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId,GrayProperties grayProperties) {
        this(serviceInstanceListSupplierProvider, serviceId,grayProperties, new Random().nextInt(1000));
    }
    public GrayRoundRobinLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
                                  String serviceId,GrayProperties grayProperties, int seedPosition) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.grayProperties = grayProperties;
        this.position = new AtomicInteger(seedPosition);
        this.defaultLoadBalancer = new RoundRobinLoadBalancer(serviceInstanceListSupplierProvider, serviceId);
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        //关闭灰度
        boolean isGrayEnabled = grayProperties.getEnabled();
        if(!isGrayEnabled){
            return defaultLoadBalancer.choose(request);
        }
        //ObjectProvider 允许安全地获取 Bean，即使它不存在也不会直接抛异常（可以通过 getIfAvailable() 判断）。
        // 获取实际实例（使用时才初始化）
        // 1. 获取原始的 ServiceInstanceListSupplier
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
        if (supplier == null) {
            return Mono.just(new EmptyResponse());
        }
        // 2. 检查是否有灰度头标记
        boolean isGrayHeaderTag = hasGrayHeaderTag(request);
        // 3. 获取所有实例，并根据请求类型过滤
        return supplier.get(request).next()
                .map(instances -> getInstances(instances, isGrayHeaderTag))
                .map(filteredInstances -> {
                    // 4. 使用 Round-Robin 选择一个实例
                    int pos = position.incrementAndGet() & Integer.MAX_VALUE;
                    ServiceInstance chosen = filteredInstances.get(pos % filteredInstances.size());
                    return new DefaultResponse(chosen);
                });
    }

    private boolean hasGrayHeaderTag(Request request) {
//        if (!(request.getContext() instanceof RequestDataContext)) {
//            return false;
//        }
//        RequestDataContext context = (RequestDataContext) request.getContext();
//        RequestData clientRequest = context.getClientRequest();
//        if (clientRequest == null || clientRequest.getHeaders() == null) {
//            return false;
//        }
//        RequestData clientReq = ((RequestDataContext) request.getContext()).getClientRequest();
//        return GrayConstant.GRAY_HEADER_TAG_VALUE_TRUE.equals(clientReq.getHeaders().getFirst(GrayConstant.GRAY_HEADER_TAG));
       return GrayConstant.GRAY_HEADER_TAG_VALUE_TRUE.equals(GrayContextHolder.getGrayTag());
    }

    private List<ServiceInstance> getInstances(List<ServiceInstance> instances,boolean isGrayHeaderTag) {
        if(isGrayHeaderTag){
            List<ServiceInstance> grayInstances = instances.stream()
                    .filter(instance -> StrUtil.isNotEmpty(instance.getMetadata().get(GrayConstant.GRAY_TAG))
                            && GrayConstant.GRAY_TAG_VALUE_TRUE.equals(instance.getMetadata().get(GrayConstant.GRAY_TAG)))
                    .collect(Collectors.toList());
            // 如果没有灰度实例，返回所有实例
            return CollUtil.isEmpty(grayInstances) ? instances : grayInstances;
        }else{
            List<ServiceInstance> generalInstances = instances.stream()
                    .filter(instance -> StrUtil.isEmpty(instance.getMetadata().get(GrayConstant.GRAY_TAG))
                            || GrayConstant.GRAY_TAG_VALUE_FALSE.equals(instance.getMetadata().get(GrayConstant.GRAY_TAG)))
                    .collect(Collectors.toList());
            // 如果没有非灰度实例，返回所有实例
            return CollUtil.isEmpty(generalInstances) ? instances : generalInstances;
        }
    }
    public String getServiceId() {
        return serviceId;
    }
}
